#pragma once
#include<mutex>
#include<map>
template<class Key, class ARG_Key, class Value, class ARG_Value>
class CMapWithLock
{
public:
	BOOL Lookup(ARG_Key key, ARG_Value& value, BOOL bIsManualUnlock = FALSE)
	{
		m_oLock.Lock();
		BOOL bResult = m_oObject.Lookup(key, value);
		if (FALSE == bIsManualUnlock)
			m_oLock.Unlock();
		return bResult;
	}

	BOOL RemoveKey(ARG_Key key, BOOL bIsLock = TRUE)
	{
		BOOL bResult;
		if (TRUE == bIsLock)
		{
			m_oLock.Lock();
			bResult = m_oObject.RemoveKey(key);
			m_oLock.Unlock();
		}
		else
		{
			bResult = m_oObject.RemoveKey(key);
		}

		return bResult;
	}

	void RemoveAll(BOOL bIsLock = TRUE)
	{
		if (TRUE == bIsLock)
		{
			m_oLock.Lock();
			m_oObject.RemoveAll();
			m_oLock.Unlock();
		}
		else
		{
			m_oObject.RemoveAll();
		}
	}

	void SetAt(ARG_Key key, ARG_Value value, BOOL bIsLock = TRUE)
	{
		if (TRUE == bIsLock)
		{
			m_oLock.Lock();
			m_oObject.SetAt(key, value);
			m_oLock.Unlock();
		}
		else
		{
			m_oObject.SetAt(key, value);
		}
	}

	void ManualUnlock()
	{
		m_oLock.Unlock();
	}

	void ManualLock()
	{
		m_oLock.Lock();
	}

	POSITION GetStartPos()
	{
		return m_oObject.GetStartPosition();
	}

	void GetNext(POSITION &pos, Key &key, Value &vlaue)
	{
		m_oObject.GetNextAssoc(pos, key, vlaue);
	}

	int GetSize()
	{
		return m_oObject.GetSize();
	}

	CMapWithLock<Key, ARG_Key, Value, ARG_Value> & operator=(CMapWithLock<Key, ARG_Key, Value, ARG_Value>& objectSrc)
	{
		m_oLock.Lock();
		m_oObject.RemoveAll();
		auto pos = objectSrc.GetStartPos();
		Key key[1];
		Value val[1];
		while (pos != nullptr)
		{
			objectSrc.GetNext(pos, key[0], val[0]);
			m_oObject.SetAt(key[0], val[0]);
		}
		m_oLock.Unlock();
		return *this;
	}

	Value&  operator[](ARG_Key key)
	{		//ASSERT_VALID(this);
		return  m_oObject[key];
	}

private:
	CMap<Key, ARG_Key, Value, ARG_Value> m_oObject;
	CCriticalSection m_oLock;
};

template<class Type>
class CListWithLock
{
public:
	POSITION AddTail(Type element)
	{
		m_oLock.Lock();
		POSITION pos = m_oList.AddTail(element);
		m_oLock.Unlock();
		return pos;
	}

	Type GetHead()
	{
		Type result;
		memset(&result, 0, sizeof(result));
		m_oLock.Lock();
		if (0 < m_oList.GetSize())
		{
			result = m_oList.GetHead();
		}
		m_oLock.Unlock();
		return result;
	}

	Type GetTail()
	{
		Type result;
		memset(&result, 0, sizeof(result));
		m_oLock.Lock();
		if (0 < m_oList.GetSize())
		{
			result = m_oList.GetTail();
		}
		m_oLock.Unlock();
		return result;
	}

	Type RemoveHead()
	{
		Type result;
		memset(&result, 0, sizeof(result));
		m_oLock.Lock();
		if (0 < m_oList.GetSize())
			result = m_oList.RemoveHead();
		m_oLock.Unlock();
		return result;
	}

	void RemoveAt(POSITION &pos)
	{
		if (pos)
		{
			m_oLock.Lock();
			m_oList.RemoveAt(pos);
			pos = nullptr;
			m_oLock.Unlock();
		}
	}

	void RemoveAll()
	{
		m_oLock.Lock();
		m_oList.RemoveAll();
		m_oLock.Unlock();
	}

	int  GetCount()
	{
		m_oLock.Lock();
		int nCount = m_oList.GetCount();
		m_oLock.Unlock();
		return nCount;
	}

	CListWithLock<Type> & operator=(CListWithLock<Type>& objectSrc)
	{
		m_oLock.Lock();
		m_oList.RemoveAll();
		auto pos = objectSrc.GetHeadPosition();
		while (pos != nullptr)
			m_oList.AddTail(objectSrc.GetNext(pos));
		m_oLock.Unlock();
		return *this;
	}

protected:
	POSITION GetHeadPosition()
	{
		return m_oList.GetHeadPosition();
	}
	Type& GetNext(POSITION &pos)
	{
		return  m_oList.GetNext(pos);
	}

private:
	CList<Type, Type&> m_oList;
	CCriticalSection m_oLock;
};

template<typename Key, class Value>
class mapwithlock
{
	typedef typename std::map<Key, Value>::iterator mappositon;
public:

	BOOL RemoveKey(Key key, BOOL bIsLock = TRUE)
	{
		BOOL bResult;
		if (TRUE == bIsLock)
		{
			m_oLock.lock();
			bResult = m_oObject.erase(key);
		}
		else
		{
			bResult = m_oObject.erase(key);
		}

		return bResult;
	}

	void RemoveAll(BOOL bIsLock = TRUE)
	{
		if (TRUE == bIsLock)
		{
			m_oLock.lock();
			m_oObject.RemoveAll();
		}
		else
		{
			m_oObject.RemoveAll();
		}
	}

	void SetAt(Key key, Value value, BOOL bIsLock = TRUE)
	{
		if (TRUE == bIsLock)
		{
			m_oLock.lock();
			m_oObject.emplace(key, value);
		}
		else
		{
			m_oObject.emplace(key, value);
		}
	}

	void ManualUnlock()
	{
		m_oLock.unlock();
	}

	void ManualLock()
	{
		m_oLock.lock();
	}

	int GetSize()
	{
		return m_oObject.size();
	}

	BOOL Lookup(Key key, Value& value, BOOL bIsManualUnlock = FALSE)
	{
		m_oLock.lock();
		auto bResult = FALSE;
		auto retvalue = m_oObject[key];
		if (FALSE == bIsManualUnlock)
			m_oLock.unlock();
		if (retvalue != nullptr)
		{
			value = std::move(retvalue);
			bResult = TRUE;
		}

		return bResult;
	}

	Value & operator[](Key key)
	{
		std::unique_lock<std::recursive_mutex> lck(m_oLock, std::adopt_lock);
		auto result = m_oObject[key];
		return result;
	}

	mappositon GetStartPos()
	{
		return m_oObject.cbegin();
	}

	mapwithlock<Key, Value> & operator=(const mapwithlock<Key, Value>& objectSrc)
	{
		m_oLock.lock();
		m_oObject.clear();
		auto pos = objectSrc.GetStartPos();
		auto count = objectSrc.GetSize();
		auto index = 0;
		while (pos != nullptr&& index < count)
		{
			m_oObject.emplace(pos->first, pos->second);
			++pos;
			++index;
		}
		m_oLock.unlock();
		return *this;
	}

	mapwithlock<Key, Value> & operator=(const mapwithlock<Key, Value>&& objectSrc)
	{
		*this = std::move(objectSrc);
		return *this;
	}

private:
	std::map<Key, Value> m_oObject;
	std::recursive_mutex m_oLock;
};
